package com.linran.wxpublic.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 微信签名
 *
 * @author lin
 * @date 2020-08-17 13:45
 */
@Slf4j
@Component
public class WxUtil {
    @Value("${wx.token}")
    private String token;
    private static final byte[] HEX_ARRAY = "0123456789abcdef".getBytes();
    @Value("${wx.token.url}")
    private String wxTokenUrl;
    @Value("${wx.appid}")
    private String wxAppId;
    @Value("${wx.secret}")
    private String wxSecrect;
    public static final String WX_TOKEN_KEY = "lin:ran:wx:token";
    private RestTemplate restTemplate = new RestTemplate();
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 验证签名
     *
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[]{token, timestamp, nonce};
        //将token、timestamp、nonce三个参数进行字典排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;
        try {
            //创建MessageDigest对象，MessageDigest通过其getInstance系列静态函数来进行实例化和初始化
            md = MessageDigest.getInstance("SHA-1");
            //将三个参数字符串拼成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        boolean checkResult = !StringUtils.isEmpty(tmpStr) && tmpStr.equals(signature);
        log.info("[签名加密] checkResult:{} timestamp:{} nonce:{} digestResult:{} signature:{} ", checkResult, timestamp, nonce, tmpStr, signature);
        return checkResult;
    }

    private String bytesToHex(byte[] bytes) {
        byte[] hexChars = new byte[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars, StandardCharsets.UTF_8);
    }

    public String getWxToken() {
        String token = redisUtil.get(WX_TOKEN_KEY);
        if (StringUtils.isEmpty(token)) {
            //刷新token
            String tokenUrl = String.format(wxTokenUrl, wxAppId, wxSecrect);
            log.info("[微信公众号] token获取url:{}", tokenUrl);
            WxTokenRsp tokenRsp = restTemplate.getForObject(tokenUrl, WxTokenRsp.class);
            if (Objects.isNull(tokenRsp) || StringUtils.isEmpty(tokenRsp.getAccessToken()) || StringUtils.isEmpty(tokenRsp.getExpiresIn())) {
                log.error("[微信公众号] token获取失败 rsp:{}", JSON.toJSONString(tokenRsp));
                return null;
            }
            log.info("[微信公众号] 刷新token:{} expiresIn:{}", tokenRsp.getAccessToken(), tokenRsp.getExpiresIn());
            //提前5分钟失效，进行提前刷新(防止使用过程中token失效)
            redisUtil.set(WX_TOKEN_KEY, tokenRsp.getAccessToken(), Long.valueOf(tokenRsp.getExpiresIn()) - 5 * 60L, TimeUnit.SECONDS);
            token = tokenRsp.getAccessToken();
        }
        log.info("[微信公众号] token:{}", token);
        return token;
    }
}
